package nftservice

import (
	"encoding/gob"
	"encoding/json"
	"github.com/astaxie/beego"
	"github.com/astaxie/beego/context"
	"github.com/spf13/cast"
	"nft-server/common"
	"nft-server/config"
	"nft-server/db"
	"nft-server/errcode"
	"nft-server/logger"
	"nft-server/nftservice/controllers"
	"nft-server/service"
	"nft-server/util"
	"time"
)

func init() {
	// business interface
	beego.Router(controllers.ApplyAssetURI, &controllers.NftController{}, "post:ApplyAsset")
	beego.Router(controllers.GetAssetURI, &controllers.NftController{}, "get:GetAssets")
	beego.Router(controllers.TransferAssetURI, &controllers.NftController{}, "post:TransferPresentAssets")
	beego.Router(controllers.VerifyAssetDetailURI, &controllers.NftController{}, "get:VerifyAssetDetail")
	beego.Router(controllers.GetAssetListURI, &controllers.NftController{}, "get:GetAssetsList")
	beego.Router(controllers.GetBlockchainURI, &controllers.NftController{}, "get:GetBlockchain")
	beego.Router(controllers.GetTransactionsURI, &controllers.NftController{}, "get:GetTransactions")
	beego.Router(controllers.GetBlockChainTypeURI, &controllers.NftController{}, "get:GetBlockChainType")
	beego.Router(controllers.GetAssetDetailURI, &controllers.NftController{}, "get:GetAssetDetail")

	beego.Router(controllers.LoginURI, &controllers.UserController{}, "post:Login")
	beego.Router(controllers.LoginUsingPhoneNumURI, &controllers.UserController{}, "post:LoginUsingPhoneNum")
	beego.Router(controllers.RegistrationURI, &controllers.UserController{}, "post:Registration")
	beego.Router(controllers.LogoutURI, &controllers.UserController{}, "get:Logout")
	beego.Router(controllers.CaptchaURI, &controllers.UserController{}, "get:GetCaptcha")
	beego.Router(controllers.ProfileURI, &controllers.UserController{}, "get:Profile")
	beego.Router(controllers.UnsubscribeURI, &controllers.UserController{}, "post:Unsubscribe")
	beego.Router(controllers.ChangepassURI, &controllers.UserController{}, "post:ChangePass")
	beego.Router(controllers.CheckUserNameURI, &controllers.UserController{}, "get:CheckUserName")
	beego.Router(controllers.CheckUserPhoneRegURI, &controllers.UserController{}, "get:CheckUserRegPhone")
	beego.Router(controllers.AuthenticationURI, &controllers.UserController{}, "post:UserAuthentication")
	beego.Router(controllers.CheckAuthenticURI, &controllers.UserController{}, "get:CheckAuthentication")
	beego.Router(controllers.VerifyPhoneNumURI, &controllers.UserController{}, "post:VerifyPhoneNum")
	beego.Router(controllers.UpdatePhoneNumURI, &controllers.UserController{}, "post:UpdatePhoneNum")
	beego.Router(controllers.ChangeProfilePhotoURI, &controllers.UserController{}, "post:UpdateProfilePhoto")
	beego.Router(controllers.GetProfilePhotoURI, &controllers.UserController{}, "get:GetProfilePhoto")

	// 管理员管理用户接口
	beego.Router(controllers.GetUsersByNameAndStatusURI, &controllers.AdminController{}, "get:GetUsersByNameAndStatus")
	beego.Router(controllers.UserURI, &controllers.AdminController{}, "get:GetUserById;delete:DeleteUserById")
	beego.Router(controllers.LockUserURI, &controllers.AdminController{}, "put:LockUser")
	beego.Router(controllers.UnLockUserURI, &controllers.AdminController{}, "put:UnlockUser")
	// management interface

	//add interface
	beego.Router(controllers.AddCollectionURI, &controllers.CollectionDetailController{}, "post:AddCollectionDetail")
	beego.Router(controllers.QueryCollectionURI, &controllers.CollectionDetailController{}, "get:QueryCollectionDetail")
	beego.Router(controllers.ModifyCollectionURI, &controllers.CollectionDetailController{}, "put:ModifyCollectionDetail")
	beego.Router(controllers.PublishCollectionURI, &controllers.CollectionDetailController{}, "put:PublishCollection")
	beego.Router(controllers.GetCollectionForHomePageURI, &controllers.CollectionDetailController{}, "get:GetCollectionInfoForHomePage")
	beego.Router(controllers.GetCollectionForRecommend, &controllers.CollectionDetailController{}, "get:GetCollectionInfoForRecommend")
	beego.Router(controllers.GetCollectionNameForMintURI, &controllers.CollectionDetailController{}, "get:GetCollectionNamesForMint")
	beego.Router(controllers.GetCollectionForPublish, &controllers.CollectionDetailController{}, "get:GetCollectionForPublish")

	//order interface
	beego.Router(controllers.QueryOrderURI, &controllers.OrderController{}, "put:QueryOrder")
	beego.Router(controllers.CancelOrderURI, &controllers.OrderController{}, "get:CancelOrder")

	beego.Router(controllers.PrePaymentURI, &controllers.PaymentController{}, "post:PrePay")
	beego.Router(controllers.PaymentWebsocket, &controllers.PaymentController{}, "post:PaymentNotifyWithWebSocket")
	//NotifyOrderURI 用于处理第三方平台支付通知--该接口无需校验
	beego.Router(controllers.PaymentNotifyURI, &controllers.PaymentController{}, "post:PaymentNotify")

	//chain
	beego.Router(controllers.RequestMintURI, &controllers.MintController{}, "post:RequestMultipleMint")
	beego.Router(controllers.BurnAssetURI, &controllers.MintController{}, "post:BurnAssets")
	beego.Router(controllers.SaveFileURI, &controllers.MintController{}, "post:SaveFile")
	beego.Router(controllers.GetFileURI, &controllers.MintController{}, "get:GetFileUrl")
	addFilter()
}

func addFilter() {
	// 普通用户需要登录
	beego.InsertFilter("/v1/admin/*", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/chain/*", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/collection/add", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/collection/query", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/collection/modify", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/chain/burnasset", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/user/unsubscribe", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/user/profile", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/user/logout", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/user/changepass", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/assets", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/assets/apply", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/assets/transfer", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/assets/get", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/assets/detail", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/file/save", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/collection/names/invisible", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/user/authentication", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/user/checkauthentication", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/user/updatephonenum", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/user/changeProfilePhoto", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/user/getProfilePhoto", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/order/query", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/payment/prepay", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/payment/ws", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/order/cancel", beego.BeforeExec, ValidateJWTFilter)
	beego.InsertFilter("/v1/collection/publish/detail", beego.BeforeExec, ValidateJWTFilter)

	// 管理员
	beego.InsertFilter("/v1/admin/*", beego.BeforeExec, ValidateAdminFilter)
	beego.InsertFilter("/v1/chain/*", beego.BeforeExec, ValidateAdminFilter)
	beego.InsertFilter("/v1/file/save", beego.BeforeExec, ValidateAdminFilter)
	beego.InsertFilter("/v1/collection/add", beego.BeforeExec, ValidateAdminFilter)
	beego.InsertFilter("/v1/collection/query", beego.BeforeExec, ValidateAdminFilter)
	beego.InsertFilter("/v1/collection/modify", beego.BeforeExec, ValidateAdminFilter)
	beego.InsertFilter("/v1/collection/names/invisible", beego.BeforeExec, ValidateAdminFilter)
	beego.InsertFilter("/v1/collection/end", beego.BeforeExec, ValidateAdminFilter)

	// 共享版不支持接口
	beego.InsertFilter("/v1/assets/verify", beego.BeforeExec, ShareModeFilter)
	beego.InsertFilter("/v1/store/blocks/summary", beego.BeforeExec, ShareModeFilter)
	beego.InsertFilter("/v1/transactions/list", beego.BeforeExec, ShareModeFilter)

}

func Start() {
	if err := db.GetDBFactory().InitDBServer(db.GaussSQLDriver, &config.GetConfig().DBConfig); err != nil {
		logger.Error("init db server error: %v", err)
	}
	// 初始化数据库数据
	if err := service.InitRoleTable(); err != nil {
		logger.Error("init role table failed: %v", err)
	}
	if err := service.InitAdminUser(); err != nil {
		logger.Error("create admin user failed: %v", err)
	}
	if err := service.InitCategoryTable(); err != nil {
		logger.Error("init category table failed: %v", err)
	}
	// session_redis需要
	gob.Register(&common.Captcha{})
	service.Init()
	service.BurnInit()
	service.MintTaskInit()
	//增加redis的初始化--暂时不初始化
	beego.Run()
}

func ValidateJWTFilter(ctx *context.Context) {
	token := ctx.Request.Header.Get("token")
	if len(token) == 0 {
		HandleErrCode(ctx, errcode.E.Certification.HavaNotLogin)
		return
	}
	claims, err := util.ParseToken(token)
	if err != nil {
		HandleErrCode(ctx, errcode.E.Certification.HavaNotLogin)
		return
	}
	// 登录是否过时
	if time.Now().After(time.Unix(claims.ExpiresAt, 0)) {
		HandleErrCode(ctx, errcode.E.Certification.HavaNotLogin)
		return
	}

	// session是否存在
	userNameSession := ctx.Input.CruSession.Get(common.SessionUserName)
	if userNameSession == nil {
		HandleErrCode(ctx, errcode.E.Certification.HavaNotLogin)
		return
	}

	// 检查token是否快要过期，如果要过期了，刷新token
	if time.Now().Add(5 * time.Minute).After(time.Unix(claims.ExpiresAt, 0)) {
		lifeTime, err := beego.AppConfig.Int("jwtTokenLifeTimeMinute")
		if err != nil {
			return
		}
		expiredTime := time.Now().Add(time.Minute * time.Duration(lifeTime))
		newToken, err := util.GetJWTToken(claims.SessionId, expiredTime)
		if err != nil {
			HandleErrCode(ctx, errcode.E.Internal.Internal.WithErr(err))
			return
		}
		ctx.Output.Header("newToken", newToken)
		ctx.Output.Header("tokenExpiredTime", cast.ToString(expiredTime.UnixMilli()))
	}
}

func ValidateAdminFilter(ctx *context.Context) {
	role, ok := ctx.Input.CruSession.Get(common.SessionRole).(string)
	if !ok {
		logger.Error("ValidateAdminFilter Assert_Error.")
		HandleErrCode(ctx, errcode.E.Internal.Internal)
		return
	}
	pwdChanged, ok := ctx.Input.CruSession.Get(common.SessionPwdChanged).(bool)
	//ok判断强制转换类型是否成功
	if !ok {
		logger.Error("ValidateAdminFilter Assert_Error.")
		HandleErrCode(ctx, errcode.E.Internal.Internal)
		return
	}
	if role != service.AdminRoleName {
		HandleErrCode(ctx, errcode.E.Certification.InsufficientPermission)
		return
	}
	// 管理员没有修改密码不能进行管理员操作
	if !pwdChanged {
		HandleErrCode(ctx, errcode.E.Base.ErrorMessage.WithMessage("请先重置初始密码"))
		return
	}
}

func ShareModeFilter(ctx *context.Context) {
	if common.BlockChainType == common.BlockChainSharedMode {
		HandleErrCode(ctx, errcode.E.Mode.SharedModeNotSupport)
		return
	}
}

func HandleErrCode(ctx *context.Context, errcode errcode.Status) {
	var errMsg string
	if len(errcode.Message) != 0 {
		errMsg = errcode.Message
	} else {
		errMsg = errcode.ErrorMessage
	}
	ctx.ResponseWriter.WriteHeader(int(errcode.Code))
	data := &controllers.Response{
		Code: controllers.FailedCode,
		Msg:  errMsg,
		Data: nil,
	}
	res, err := json.MarshalIndent(data, "", "\t")
	if err != nil {
		logger.Error("HandleErrCode MarshalIndent error: %v", err)
	}
	ctx.ResponseWriter.Write(res)
}
